/*
 * @(#)AbstractCompoundMetaObject.java	1.0 2000/12/28
 *
 * Copyright 2000 Thomas Barnekow. All Rights Reserved.
 *
 * This software is the proprietary information of Thomas Barnekow.
 * Use is subject to license terms.
 */

package tb.provider;

import java.util.*;

/**
 * This is an abstract base class for {@link CompoundMetaObject}s. Concrete
 * subclasses must override {@link #createElementCollection}.
 *
 * @author Thomas Barnekow
 * @version 1.0
 */
public abstract class AbstractCompoundMetaObject
  extends AbstractMetaObject implements CompoundMetaObject
{

  /**
   * This compound meta-object's elements.
   */
  private Collection m_elements;

  /**
   * The element map.
   */
  private Map m_map;

  /**
   * Create a top-level (uncontained) {@link CompoundMetaObject}.
   */
  public AbstractCompoundMetaObject(Comparable id) {
    super(id);
  }

  /**
   * Create a {@link CompoundMetaObject} contained in the given <code>container</code>.
   */
  public AbstractCompoundMetaObject(Comparable id, CompoundMetaObject container) {
    super(id, container);
  }

  /*
   * CompoundMetaObject operations
   */

  public final boolean add(MetaObject element) {
    if (element == null)
      throw new NullPointerException("MetaObject must not be null");

    Map map = getElementMap();
    Comparable id = element.getId();
    if (map.containsKey(id))
      return false;

    getElements().add(element);
    map.put(id, element);
    return true;
  }

  public final void clear() {
    getElements().clear();
    getElementMap().clear();
  }

  public boolean contains(MetaObject element) {
    return getElementMap().containsKey(element.getId());
  }

  public boolean isEmpty() {
    return getElements().isEmpty();
  }

  public Iterator iterator() {
    return getElements().iterator();
  }

  public final boolean remove(MetaObject element) {
    boolean result = getElements().remove(element);
    getElementMap().remove(element.getId());
    return result;
  }

  public int size() {
    return getElements().size();
  }

  public MetaObject get(Comparable id) {
    return (MetaObject) getElementMap().get(id);
  }

  /**
   * Get a {@link java.util.Collection} of this compound object's elements'
   * declarations.
   */
  public Object getDeclaration() {
    Collection collection = new ArrayList();

    Iterator elements = iterator();
    while (elements.hasNext()) {
      MetaObject element = (MetaObject) elements.next();
      collection.add(element.getDeclaration());
    }

    return Collections.unmodifiableCollection(collection);
  }

  /**
   * Factory method creating a {@link java.util.Collection} instance that will be used
   * to store this compound object's elements. Override this method in order to
   * create the actual collection. Return a {@link java.util.List} instance, e.g., an instance
   * of {@link java.util.ArrayList}, if you want the elements to be ordered in the
   * sequence they were added in. Return an instance of <code>TreeSet</code>, if you want
   * the elements to be sorted in <i>natural order</i>, i.e., by <i>id</i>
   * (see {@link MetaObject#getId}.) Wrap your collection using the
   * <code>Collections.unmodifiableCollection(...)</code> method in case you
   * want to return an unmodifiable collection. The optional operations, e.g.,
   * {@link #add} and {@link #remove}, will then throw an {@link UnsupportedOperationException}.
   */
  protected abstract Collection createElementCollection() throws Exception;

  /*
   * Private helpers
   */

  /**
   * Get the element {@link java.util.Collection}. Create one calling
   * {@link #createElementCollection} if necessary.
   */
  private Collection getElements() {
    if (m_elements == null) {
      try {
        m_elements = createElementCollection();
      } catch (Exception ex) {
        throw new RuntimeException(ex.toString());
      }
    }
    return m_elements;
  }

  /**
   * Create the element {@link java.util.Map}.
   */
  private Map createElementMap() {
    Map map = new HashMap();

    Iterator iterator = getElements().iterator();
    while (iterator.hasNext()) {
      MetaObject element = (MetaObject) iterator.next();
      map.put(element.getId(), element);
    }

    return map;
  }

  /**
   * Get the element {@link java.util.Map}. Create it if necessary.
   */
  private Map getElementMap() {
    if (m_map == null) {
      m_map = createElementMap();
    }
    return m_map;
  }
}